/*  File Name	: EngineMonitor.cpp
 *  Author		: Javie Pong
 *	 E-mail		: 823960168@qq.com
 *  Created on	: 2012-7-1
 *  Description:
 */
#include <algorithm>
#include "EngineMonitor.h"
#include "EpollReactor.h"
#include "MySqlDB.h"

EngineMonitor::EngineMonitor()
{
	this->gate_map_.clear();
	this->engineservice_map_.clear();
	this->player_map_.clear();
	this->table_map_.clear();
	this->table_vec_.clear();
	this->game_type_ = 0;
	this->db_map_.clear();
}

EngineMonitor::~EngineMonitor()
{
	DBMap::iterator iter = this->db_map_.begin();
	for(;iter!=this->db_map_.end();iter++)
	{
		DELETE(iter->second);
	}
}

EngineMonitor* EngineMonitor::instance_ = NULL;

EngineMonitor *EngineMonitor::instance(void)
{
	if(NULL==instance_)
	{
		instance_ = new EngineMonitor();
	}
	return instance_;
}

EngineMonitor::TablePool &EngineMonitor::table_pool(void)
{
	return this->table_pool_;
}

EngineMonitor::PlayerPool &EngineMonitor::player_pool(void)
{
	return this->player_pool_;
}

EngineMonitor::CardPool &EngineMonitor::card_pool(void)
{
	return this->card_pool_;
}

EngineMonitor::PokerPool &EngineMonitor::poker_pool(void)
{
	return this->poker_pool_;
}

EngineMonitor::EngPlayerMap &EngineMonitor::player_map(void)
{
	return this->player_map_;
}

EngineMonitor::GateMap &EngineMonitor::gate_map(void)
{
	return this->gate_map_;
}

int EngineMonitor::room_id(void)
{
	return this->cfg()["uid"].asInt();
}

std::string EngineMonitor::room_name(void)
{
	return this->cfg()["name"].asString();
}

const Json::Value &EngineMonitor::cfg(void)
{
#ifdef DEBUG
	return CONFIG_INSTANCE->engine()["debug"][this->line_id_];

#else
	return CONFIG_INSTANCE->engine()["online"][this->line_id_];
#endif
}

const int EngineMonitor::line(void)
{
	return this->line_id_;
}

EnginePlayer* EngineMonitor::find_player(int role_id)
{
	//LockGuardReturn(RT_MUTEX, lock, this->rt_mutex_, NULL);
	EngPlayerMap::iterator iter= this->player_map_.find(role_id);
	if(iter==this->player_map_.end())
	{
		return NULL;
	}
	return iter->second;
}

int EngineMonitor::bind_player(EnginePlayer *player)
{
	if (NULL == player)
	{
		return -1;
	}
	//LockGuardReturn(RT_MUTEX, lock, this->rt_mutex_, -1);
	EngPlayerMap::iterator iter = this->player_map_.find(player->role_id());
	if (iter == this->player_map_.end())
	{
		this->player_map().insert(std::pair<int,EnginePlayer*>(player->role_id(),player));
		return 0;
	}
	return -1;
}

int EngineMonitor::unbind_player(int role_id)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	EngPlayerMap::iterator iter= this->player_map_.find(role_id);
	if(iter==this->player_map_.end())
	{
		return -1;
	}
	this->player_pool().pop(iter->second);
	this->player_map_.erase(role_id);
	return 0;
}

EngineService *EngineMonitor::find_gate(int gate_id)
{
	//LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,NULL);
	GateMap::iterator iter= this->gate_map_.find(gate_id);
	if(iter==this->gate_map_.end())
	{
		return NULL;
	}
	return iter->second;
}

int EngineMonitor::handle_close(EngineService *es)
{
	LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	this->unbind_gate(es->gate_id());
	return super::handle_close(es);
}

int EngineMonitor::bind_gate(int id,EngineService *es)
{
	if (NULL != es)
	{
		//LockGuardReturn(RT_MUTEX, lock, this->rt_mutex_, -1);
		es->gate_id(id);
		this->gate_map_.insert(std::pair<int, EngineService*>(id, es));
		return 0;
	}
	return -1;
}

int EngineMonitor::unbind_gate(int gate_id)
{
	//LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	GateMap::iterator iter= this->gate_map_.find(gate_id);
	if(iter==this->gate_map_.end())
	{
		return -1;
	}
	this->gate_map_.erase(gate_id);
	return 0;
}

void EngineMonitor::game_exit_save_data(void)
{
	LOG((INFO_LOG," EngineMonitor::game_exit_save_data"));
	for(EngPlayerMap::iterator iter=this->player_map_.begin();
			iter!=this->player_map_.end();iter++)
	{
		iter->second->save_data();
	}
}

Table *EngineMonitor::find_table(int id)
{
	TableMap::iterator iter= this->table_map_.find(id);
	if(iter==this->table_map_.end())
	{
		return NULL;
	}
	return iter->second;
}

int EngineMonitor::assign_table(void)
{
	std::sort(this->table_vec_.begin(),this->table_vec_.end(),EngineMonitor::table_sort);

	int full=-1,empty= 0;
	int i=0;
	for(;i<(int)this->table_vec_.size();i++)
	{
		if(this->table_vec_[i]->is_full())
		{
			full=i;
			continue;
		}
		if(this->table_vec_[i]->player_count()==0)
		{
			empty = 1;
			break;
		}
	}
	int start=full+1;
	int end=(empty==0)?(i-1):((start<i)?(i-1):i);

	if(start>=(int)this->table_vec_.size() || start>end)
	{
		return -1;
	}

	int index = rand()%(end-start+1)+start;
	return index;
}

int EngineMonitor::table_sort(Table *t1,Table *t2)
{
	return t1->player_count() > t2->player_count();
}

int EngineMonitor::make_up_table_info(MessageBlock *mb)
{
	if(NULL==mb)
	{
		return -1;
	}

	*mb << (int)this->table_map_.size();
	TableMap::iterator iter= this->table_map_.begin();
	for(;iter!=this->table_map_.end();iter++)
	{
		if(NULL!=iter->second)
		{
			iter->second->make_up_table_info(mb);
		}
	}
	return 0;
}

int EngineMonitor::make_up_player_info(MessageBlock *mb)
{
//	if(NULL==mb)
//	{
//		return -1;
//	}
//	EnginePlayer *player = NULL;
//	*mb << (int)this->player_map_.size();
//	for(EngPlayerMap::iterator iter=this->player_map_.begin();
//			iter!=this->player_map_.end();iter++)
//	{
//		player = iter->second;
//		*mb << player->role_id() << player->base_info().sex
//				<< player->base_info().account
//				<< player->base_info().total_chips;
//	}
	return 0;
}

int EngineMonitor::broadcast_to_room(int cmd,MessageBlock *mb)
{
	EnginePlayer *player = NULL;
	EngPlayerMap::iterator iter=this->player_map_.begin();
	for(;iter!=this->player_map_.end();iter++)
	{
		player = iter->second;
		//if(player->table_id()==0)
		{
			player->respond_success_to_client(cmd,mb);
		}
	}
	return 0;
}

int EngineMonitor::respond_to_gate(int cmd,int gate,int role_id,int error,MessageBlock *mb)
{
	MessageBlock *send_mb =NULL;
	this->msg_pool()->pop(send_mb);
	*send_mb << gate << role_id << cmd << error;

	if(NULL!=mb)
	{
		send_mb->write(mb->rd_ptr(),mb->data_len());
	}
	return this->respond_list()->put_obj(send_mb);
}

int EngineMonitor::RoutineTimer::handle_timeout(void)
{
	this->monitor->conn_gate();
	return 0;
}

int EngineMonitor::conn_gate(void)
{
	for(int i=0;i<(int)this->cfg()["gateway"].size();i++)
	{
		int id = this->cfg()["gateway"][i][0u].asInt();
		if(this->gate_map_.find(id)==this->gate_map_.end())
		{
			std::string ip = this->cfg()["gateway"][i][1u].asString();
			int port = this->cfg()["gateway"][i][2u].asInt();
			EngineService *svc = NULL;

			if(this->connector_->connect(ip,port,svc)>=0)
			{
				LOG((INFO_LOG,"EngineMonitor::conn_gate[%s:%d]",ip.c_str(),port));

				LockGuardReturn(RT_MUTEX, lock, this->rt_mutex_, -1);
				this->bind_gate(id,svc);
				MessageBlock mb(sizeof(int) * 10);
				mb << 0 << ENGINE_REGISTER  << 0
						<< this->game_type_ << this->room_id()
						<< this->cfg()["limit_member"].asInt()
						<< this->cfg()["min_chips_bring_in"].asInt()
						<< this->cfg()["max_chips_bring_in"].asInt();
				int len = mb.data_len();
				memcpy(mb.base_ptr(),&len,sizeof(int));
				svc->send_msg(&mb,mb.data_len());
			}
			else
			{
				if(svc !=NULL)
				{
					svc->handle_close();
				}
				LOG((ERROR_LOG,"EngineMonitor::conn_gate connect [%s:%d] failed",ip.c_str(),port));
			}
		}
	}
	return 0;
}

int EngineMonitor::register_engine(MessageBlock *mb)
{
	int gateid = 0,result = 0;
	*mb >> gateid >> result;
	if(result<0)
	{
		this->unbind_gate(gateid);
	}
	return 0;
}

int EngineMonitor::init(int line_id,int list_size,int block_size,int pool_size,
		int growth_size,std::string pool_name)
{
	if(NULL==this->local_db())
	{
		LOG((ERROR_LOG,"EngineMonitor::init-->error can't connect db [%d]",line_id));
		sleep(1);
		return -1;
	}

	this->line_id_ = line_id;
	this->game_type_ = this->cfg()["gametype"].asInt();

	super::init(list_size,block_size,pool_size,growth_size,pool_name);

	this->acceptor_ = new EngineAcceptor(this);
	if(NULL==this->acceptor_)
	{
		return -1;
	}
	this->connector_ = new GateConnector(this);
	if(NULL==this->connector_)
	{
		return -1;
	}

	this->player_map_.clear();
	this->player_pool_.set_growth_size(512);
	this->poker_pool_.set_growth_size(128);

	//request thread
	int req_thr = this->cfg()["request_thr"].asInt();
	this->create_request_thr(req_thr,"EngineMonitor::request",false);

	//respond thread
	int res_thr = this->cfg()["respond_thr"].asInt();
	this->create_respond_thr(res_thr,"EngineMonitor::respond",false);

	//timer reactor
	timer_reactor_ = new Reactor(new EpollReactor(20,102400,true));
	this->create_thread(1,this,EngineMonitor::run_timer_reactor_loop,
			"EngineMonitor::timer",false);

	this->reactor(Reactor::instance());
	if(NULL==this->reactor()->implement())
	{
		this->reactor()->implement(new EpollReactor(10000,102400));
	}

	this->routine_timer_.monitor = this;
	this->routine_timer_.reactor(this->timer_reactor());

	this->table_map_.clear();
	Table *table = NULL;
	int table_amount = this->cfg()["table"].asInt();
	for(int i =0;i<table_amount;i++)
	{
		if(this->table_pool_.pop(table)!=0)
		{
			LOG((ERROR_LOG,"EngineMonitor::init-->pop error"));
			return -1;
		}
		table->init(this);

		TableBaseInfo info;
		info.table_id = i+1;
		info.cost = this->cfg()["cost"].asInt();
		info.min_bet = this->cfg()["min_bet"].asInt();
		info.max_chips_ = this->cfg()["max_chips_bring_in"].asInt();
		info.min_chips_ = this->cfg()["min_chips_bring_in"].asInt();
		table->set_base_info(info);

		this->table_map_.insert(std::pair<int,Table*>(table->table_id(),table));
		this->table_vec_.push_back(table);
	}
	return 0;
}

int EngineMonitor::start()
{
	super::start();

	int port = this->cfg()["port"].asInt();
	if(this->acceptor_->open(port,this->reactor())!=0)
	{
		return -1;
	}

	this->routine_timer_.schedule_timer(TimeValue(2,0),TimeValue(5,0));

	TableMap::iterator iter= this->table_map_.begin();
	for(;iter!=this->table_map_.end();iter++)
	{
		iter->second->start_timer();
	}

#ifndef DEBUG
	this->reactor()->run_reactor_loop();
	this->wait_thread_exit();
	this->game_exit_save_data();

#endif
	return 0;
}

int EngineMonitor::stop(void)
{
	LOG((INFO_LOG,"EngineMonitor::stop"));
	this->reactor()->end_reactor_loop();
	this->routine_timer_.cancel_timer();
	this->timer_reactor_->end_reactor_loop();
	return super::stop();
}

int EngineMonitor::run_request_loop(void)
{
	while(true)
	{
		if(this->reactor()->run_status()==2)
		{
			LOG((INFO_LOG,"EngineMonitor::run_request_loop exit"));
			return 0;
		}

		MessageBlock *mb = NULL;
		this->request_mb_pop(mb);
		if (NULL != mb)
		{
			this->process_request(mb);
			this->msg_pool()->push(mb);
		}
		else
		{
			usleep(1000);
		}
	}
	return 0;
}

int EngineMonitor::run_respond_loop(void)
{
	while(true)
	{
		if(this->reactor()->run_status()==2)
		{
			LOG((INFO_LOG,"EngineMonitor::run_respond_loop exit"));
			return 0;
		}

		MessageBlock *mb = NULL;
		this->respond_mb_pop(mb);
		if(NULL!=mb)
		{
			this->process_respond(mb);
			this->msg_pool()->push(mb);
		}
		else
		{
			usleep(1000);
		}
	}
	return 0;
}

void EngineMonitor::collection_info(void)
{

}

MySqlDB *EngineMonitor::local_db(void)
{
	return this->find_db(NULL);
}

std::string EngineMonitor::db_key(std::string ip,int port)
{
	char p[20]={0};
	sprintf(p,"%d",port);
	return (ip+std::string(p));
}

MySqlDB *EngineMonitor::find_db(EnginePlayer *player)
{
	int dbport = 0;
	std::string dbip = "";
	std::string dbname = "";
	std::string user = "";
	std::string pwd = "";

	if(NULL==player)
	{
		const Json::Value &cfg = CONFIG_INSTANCE->dbserver()["localdb"];
		dbip = "127.0.0.1";
		dbport = cfg["port"].asInt();
		dbname = cfg["dbname"].asString();
		user = cfg["user"].asString();
		pwd = cfg["passwd"].asString();
	}
	else
	{
		dbip = player->dbinfo().ip;
		dbport = player->dbinfo().port;
		dbname = player->dbinfo().dbname;
		user = player->dbinfo().user;
		pwd = player->dbinfo().pwd;
	}

	std::string key = this->db_key(dbip,dbport);
	DBMap::iterator iter=this->db_map_.find(key);
	if(iter!=this->db_map_.end())
	{
		return iter->second;
	}

	MySqlDB *db = new MySqlDB();
	if(db->init(dbip,dbport,dbname,user,pwd)<0)
	{
		DELETE(db);
		return NULL;
	}
	this->db_map_.insert(std::pair<std::string,MySqlDB*>(key,db));
	return db;
}

int EngineMonitor::run_timer_reactor_loop(void)
{
	LOG((DEBUG_LOG,"EngineMonitor::run_timer_reactor_loop[%d]",this->line_id_));
	this->timer_reactor_->implement()->run_status(1);
	while (true)
	{
		//LOG((INFO_LOG,"EngineMonitor::run_timer_reactor_loop[%d]",this->timer_reactor_->run_status()));
		if (this->timer_reactor_->run_status() == 2)
		{
			LOG((INFO_LOG,"EngineMonitor::run_timer_reactor_loop exit"));
			return 0;
		}
		this->timer_reactor_->handle_events();
	}
	return 0;
}

int EngineMonitor::run_timer_reactor_loop(void *p)
{
	EngineMonitor *monitor = (EngineMonitor*)p;
	if(NULL==monitor)
	{
		return 0;
	}
	return monitor->run_timer_reactor_loop();
}

Reactor *EngineMonitor::timer_reactor(void)
{
	return this->timer_reactor_;
}

int EngineMonitor::process_respond(MessageBlock *mb)
{
	int gate_id=0,role_id=0,cmd = 0;
	*mb >>gate_id >>role_id >>cmd;
	LOG((DEBUG_LOG,"EngineMonitor::process_respond-->[cmd:%d][gate:%d][%d]",
			cmd,gate_id,role_id));

	//LockGuardReturn(RT_MUTEX,lock,this->rt_mutex_,-1);
	EngineService *es = this->find_gate(gate_id);
	if (NULL!=es)
	{
		mb->reset_rd_ptr();
		int len = mb->data_len();
		::memcpy(mb->base_ptr(), (char*) &len, sizeof(int));
		es->send_msg(mb, mb->data_len());
	}
	else
	{
		LOG((ERROR_LOG,"EngineMonitor::process_respond-->[cmd:%d][gate:%d][%d]",
				cmd,gate_id,role_id));
	}
	return 0;
}

int EngineMonitor::process_request(MessageBlock *mb)
{
	int svc_id=0,gate_id=0,role_id=0,cmd = 0,remain=0;
	*mb >>svc_id>>gate_id >>role_id >>cmd >>remain;

	LOG((DEBUG_LOG,"EngineMonitor::process_request-->[cmd:%d][gate:%d][role:%d]",
			cmd,gate_id,role_id));

	EngineService *es = this->find_service(svc_id);
	if(NULL==es)
	{
		LOG((ERROR_LOG,"EngineMonitor::process_request-->can't find svc"));
		return 0;
	}

	LockGuardReturn(RT_MUTEX, lock, this->rt_mutex_, -1);

	if(cmd==RETURN_ENGINE_REGISTER)
	{
		return this->register_engine(mb);
	}

	EngineService *gate = this->find_gate(gate_id);
	if (NULL == gate)
	{
		this->bind_gate(gate_id,es);
	}
	else if (gate != es)
	{
		gate = es;
	}

	EnginePlayer *player = this->find_player(role_id);
	if (NULL==player)
	{
		if (cmd != CLIENT_ENTER_ROOM)
		{
			LOG((ERROR_LOG,"EngineMonitor::process_request-->can't find player[%d]",role_id));
			return -1;
		}
		if(this->player_pool().pop(player)!=0 || player==NULL)
		{
			LOG((ERROR_LOG,"EngineMonitor::process_request-->pop error"));
			return -1;
		}
		player->role_id(role_id);
		player->monitor(this);
	}

	switch(cmd)
	{
		case CLIENT_ENTER_ROOM:
		{
			return player->enter_room(gate_id,mb);
		}
//		case CLIENT_SWITCH_ROOM:
//		{
//			return player->switch_room(mb);
//		}
		case CLIENT_LOG_OUT:
		case CLIENT_EXIT_ROOM:
		{
			return player->exit_room(cmd,mb);
		}
		case CLIENT_ENTER_TABLE:
		{
			return player->enter_table(mb);
		}
		case CLIENT_EXIT_TABLE:
		{
			return player->exit_table(mb,true);
		}
		case CLIENT_START_GAME:
		{
			return player->start_game(mb);
		}
		case CLIENT_BET:
		{
			return player->bet(mb);
		}
		case CLIENT_RAISE:
		{
			return player->raise(mb);
		}
		case CLIENT_ALL_IN:
		{
			return player->all_in(mb);
		}
		case CLIENT_FOLD:
		{
			return player->fold(mb);
		}
		case CLIENT_CALL:
		{
			return player->call(mb);
		}
		case CLIENT_CHECK:
		{
			return player->check(mb);
		}
		case CLIENT_MODIFY_CHIPS:
		{
			return player->modify_chips(mb);
		}
		case CLIENT_UPLOAD_PIC:
		{
			break;
		}
		case CLIENT_RECHARGE:
		case CLIENT_QUERY_CHIPS:
		case CLIENT_EXCHANGE_CHIPS:
		{
			break;
		}
		default:
		{
			LOG((ERROR_LOG,"EngineMonitor::process_request-->unknown[cmd:%d][role:%d]",cmd,role_id));
			break;
		}
	}
	return 0;
}


